(() => {
	let assembler;

	/**@type { HTMLDivElement } */
	let errorDiv = document.getElementById("error");

	/**@type { HTMLDivElement } */
	let warningDiv = document.getElementById("warning");

	let buildBtn = document.getElementById("openDir");
	buildBtn.addEventListener("click", Init);

	buildBtn = document.getElementById("build");
	buildBtn.addEventListener("click", Build);

	/**@type { FileSystemDirectoryHandle } */
	let dirHandle;
	/**@type { Map<string, FileSystemFileHandle> } */
	let fileHandle = new Map();

	async function Init() {
		if (!'showDirectoryPicker' in self) {
			// The `showOpenFilePicker()` method of the File System Access API is supported.
			console.log("不支持读写文件API");
			return;
		}

		/**@type { FileSystemDirectoryHandle } */
		dirHandle = await self.showDirectoryPicker({ mode: "readwrite" });
		fileHandle.clear();

		if (!assembler) {
			assembler = new zgassembler();
			assembler.fileUtils.ReadFile = ReadFile;
			assembler.fileUtils.SaveFile = SaveFile;
			assembler.fileUtils.BytesToString = BytesToString;
			assembler.fileUtils.PathType = PathType;
		}

		buildBtn.disabled = false;
	}

	async function Build() {
		let data = await ReadFile("config.js");
		if (!data) {
			ShowMessage("error", "找不到配置文件 config.js");
			return;
		}

		data = BytesToString(data);
		const func = new Function(data);
		const config = func();

		if (config?.platform)
			assembler.SwitchPlatform(config.platform);

		const buffer = await assembler.fileUtils.ReadFile(config.source);
		const text = assembler.fileUtils.BytesToString(buffer);

		/**@type {Int16Array | undefined} */
		const result = await assembler.CompileText(text, config.source);
		if (result) {
			config.ProcessResult?.(result);
			const fileData = ProcessResult(result);
			assembler.fileUtils.SaveFile(config.output, fileData);
		}

		/**@type {OutDiagnosticMsg[]} */
		let errorMsg = "";
		let errors = assembler.diagnostic.GetExceptions();
		for (let i = 0; i < errors.length; i++) {
			const e = errors[i];
			errorMsg += `文件: ${e.filePath}\n`;
			errorMsg += `行号: ${e.line}\n`;
			errorMsg += `${e.message}\n`;
		}
		ShowMessage("error", errorMsg);

		errorMsg = "";
		errors = assembler.diagnostic.GetWarnings();
		for (let i = 0; i < errors.length; i++) {
			const e = errors[i];
			errorMsg += `文件: ${e.filePath}\n`;
			errorMsg += `行号: ${e.line}\n`;
			errorMsg += `${e.message}\n`;
		}
		ShowMessage("warning", errorMsg);
	}

	/**
	 * 
	 * @param {Int16Array} data 
	 */
	function ProcessResult(data) {
		const fileData = new Uint8Array(data.length);
		for (let i = 0; i < data.length; i++) {
			if (data[i] < 0)
				fileData[i] = 0;
			else
				fileData[i] = data[i];
		}
		return fileData;
	}

	/**
	 * 
	 * @param {string} file 
	 * @returns 
	 */
	async function ReadFile(file) {
		try {
			let handle = await dirHandle.getFileHandle(file);
			handle = await handle.getFile();
			return new Uint8Array(await handle.arrayBuffer());
		} catch (e) {
			console.error(e);
			ShowMessage("error", `文件 ${file} 不存在`);
			return;
		}
	}

	/**
	 * 
	 * @param {string} filePath 
	 * @param {Uint8Array} data 
	 */
	async function SaveFile(filePath, data) {
		let handle = await dirHandle.getFileHandle(filePath, { create: true });
		handle = await handle.createWritable();
		await handle.write(data);
		await handle.close();
	}

	/**
	 * 
	 * @param {string} path 
	 * @returns 
	 */
	async function PathType(path) {
		let index = 0;
		while (true) {
			try {
				switch (index) {
					case 0:
						await dirHandle.getFileHandle(path);
						return "file";
					case 1:
						await dirHandle.getDirectoryHandle(path);
						return "path";
					default:
						return "none";
				}
			} catch (e) { }
			index++;
		}
	}

	function BytesToString(bytes) {
		return new TextDecoder().decode(bytes);
	}

	/**
	 * 显示错误
	 * @param {"error" | "warning"} type 
	 * @param {"string"} msg 
	 */
	async function ShowMessage(type, msg) {
		switch (type) {
			case "error":
				errorDiv.innerText = msg;
				break;
			case "warning":
				warningDiv.innerText = msg;
				break;
		}
	}
})();
